Concurrent programming for shared-memory multiprocessors can include the ability for multiple threads to access the same data. The multiple threads execute on multiple processors, multiple processor cores, or other classes of parallelism that are attached to a memory shared between the processors. The shared-memory model is the most commonly deployed method of multithread communication. It allows multithreaded programs to be created in much the same way as sequential programming, which is a benefit because concurrent programming is itself notoriously difficult. In order to implement the shared-memory model, concurrent programming uses care to avoid concurrent access and use of shared data that can create undesirable conditions such as races and the like.
Locks are a common solution to avoid the problem of concurrent access to shared data. Locks are centered on a premise that variables accessed by one thread will also be accessed by other threads, and thus the variable can only be used by one thread at a time. Locks allow one thread to take control of a variable and prevent other threads from changing the variable until it is unlocked. Lock-based protocols, while popular, are often considered difficult to use. Using locks in a coarse-grained way protects relatively large amounts of data, but generally their use does not scale. Threads block one another even when they do not interfere, and the locks become a source of contention. Alternatively, using locks in a more fine-grained way while mitigating scalability issues introduces other problems because the locking conventions to ensure correctness and avoid deadlocks become complex and error prone.
Another solution is to implement applications using transactional memory, such as a software transactional memory that provides semantics in a software runtime library and/or runtime execution environment and/or using compilers. Transactional memory is a concurrency control mechanism for controlling access to shared memory based on the premise that variables used by one thread will likely not be accessed by other threads, and thus the variable can be shared without harsh ramifications to the scalability of the program.
One significant benefit of transactional memory over coarse-lock-based protocols is increased concurrency. In transactional memory, no thread needs to wait for access to data, and different threads can safely and simultaneously modify disjoint parts of a data structure that would normally be protected under the same lock. Despite the overhead of retrying transactions that fail, in most realistic concurrent programs conflicts arise rarely enough that there is an immense performance gain over course-grained lock-based protocols on even small numbers of processors or processor cores.
Problems can occur in the use of transactional memory, however, if an atomic block includes a side-effecting action. In general, a side-effecting action modifies some state visible outside of the current thread. Common examples of side-effecting actions include input/output, system calls, legacy code actions, kernel actions, device management, actions in other domains outside of the managed environment, and so on. Difficulties arise if the transaction is aborted and re-executed, because the side-effecting action is re-executed and can be re-executed multiple times upon repeated failures. Non-idempotent side effects pose the greatest difficulties. For example, an atomic block including the action of increment-a-variable and the side-effecting action of print-the-variable may repeatedly fail and re-execute as a result of memory collisions with other threads. In each re-execution the variable is printed, which is undesirable if the intent was to print the variable only once.
Others have attempted to address the issue of side-effecting actions used in transactional memory. One popular solution is simply to forbid the use of such side-effecting actions, but many researchers generally agree that limitations on programmability and composition are unacceptable in general use. Other proposed solutions defer the action until it is likely to commit, but many researchers believe that reordering of actions leads to unintended consequences. Similarly, associating a compensation block with the action reduces isolation and provides another source for bugs. Another proposed solution is to not allow transactions with side-effecting action to fail and resolving all collisions in favor of the transaction with the side-effecting actions. Of course, no more than one transaction with a side-effecting action is allowed at once. Still another solution is to break atomicity and isolation of the transaction. All of these proposed solutions, as well as others, are restricting in different ways and require significant effort from the programmer. Researchers generally agree that this problem remains unsolved.